package com.tuocent.dagv2.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tuocent.dagv2.common.AjaxResult;
import com.tuocent.dagv2.common.entity.LoginUser;
import com.tuocent.dagv2.common.entity.LoginUserInfo;
import com.tuocent.dagv2.common.entity.Transfer;
import com.tuocent.dagv2.common.utils.BCrypt;
import com.tuocent.dagv2.common.utils.PublicUtil;
import com.tuocent.dagv2.common.utils.SnowFlakeID;
import com.tuocent.dagv2.common.utils.TCUtil;
import com.tuocent.dagv2.common.utils.redis.RedisCache;
import com.tuocent.dagv2.constant.Constants;
import com.tuocent.dagv2.constant.PublicConst;
import com.tuocent.dagv2.entity.*;
import com.tuocent.dagv2.mapper.UsersMapper;
import com.tuocent.dagv2.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.ui.Model;
import org.springframework.util.DigestUtils;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 * 用户列表 服务实现类
 * </p>
 *
 * @author Vincent
 * @since 2025-07-22
 */
@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements IUsersService {

    @Resource
    private RedisCache redis;
    @Resource
    private SnowFlakeID snowFlakeID;
    @Resource
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private IValidateCodeService validateCodeService;
    @Autowired
    private ILoginLogsService loginLogsService;
    @Autowired
    private IUserRoleService userRoleService;
    @Autowired
    private IProjectsService projectsService;
    @Autowired
    private IRoleService roleService;

    private final String LOGIN_COUNT = "_login_count";

    @Override
    public AjaxResult userLogin(HttpServletRequest request, LoginUser userInfo, HttpSession session) {
        if ("".equals(userInfo.getAccount()) ||
                "".equals(userInfo.getPassword()) ||
                "".equals(userInfo.getValidateCode())) {
            return AjaxResult.error("用户账号、密码或验证码不可为空");
        }
        //登录次数检测
        String account = userInfo.getAccount();
        if (!getUserLoginErr(account)) {
            loginLogsService.loginLog(request, account, "登录失败次数超限", "失败");
            int login_wait = 1;
            Map<String, Integer> map = redis.get(account + LOGIN_COUNT);
            if (map != null) {
                login_wait = map.get("login_wait");
            }
            return AjaxResult.error("登录失败次数超限，请等待" + login_wait + "分钟后再试");
        }

        //验证码检测
        if (!validateCodeService.check(userInfo.getKey(), userInfo.getValidateCode())) {
            return AjaxResult.error("输入的验证码不正确");
        }

        //用户信息检测
        Users user = getOne(new QueryWrapper<Users>()
                .eq("username", userInfo.getAccount())
                .eq("is_del", "0")
                .last("limit 1"));
        if (user == null) {
            return AjaxResult.error("用户不存在或密码错误");
        }
        //检测密码
        String pwd = userInfo.getPassword() + user.getCtime();
        String hashPass = BCrypt.encrypt(pwd);
        if (!BCrypt.matches(pwd, user.getPassword())) {
            return AjaxResult.error("用户不存在或密码错误");
        }
        if (!"1".equals(user.getEnabled())) {
            return AjaxResult.error("您的帐号还没有激活，请与管理员联系");
        }

        //获取用户角色
        List<UserRole> userRoles = userRoleService.list(new QueryWrapper<UserRole>().eq("user_id", user.getId()));
        Collection<String> roles = new ArrayList<>();
        for (UserRole userRole : userRoles) {
            roles.add(userRole.getRoleId());
        }

        String spm = TCUtil.getOrderNo("SP.");
        LoginUserInfo loginUserInfo = new LoginUserInfo();
        loginUserInfo.setUserCode(user.getUsername());
        loginUserInfo.setUserId(user.getId());
        loginUserInfo.setUserName(user.getRealName());
        loginUserInfo.setIp(TCUtil.getClientIp(request));

        loginUserInfo.setRoles(roles);
        loginUserInfo.setSpm(spm);
        redis.set(session.getId(), loginUserInfo, 3600L);

        session.setAttribute("spm", spm);

        Map<String, String> map = new HashMap<>();
        map.put("username", user.getUsername());
        map.put("realName", user.getRealName());
        map.put("spm", spm);
        map.put("url", "/");
        user.setLastLoginIp(TCUtil.getClientIp(request));
        user.setLastLoginTime(LocalDateTime.now());
        updateById(user);
        loginLogsService.loginLog(request, account, "用户登录成功", "成功");
        redis.deleteObject(account + "_login_info");
        return AjaxResult.success(map);
    }

    @Override
    public String userInfo(Model model) {
        List<Projects> projectsList = projectsService.list(new QueryWrapper<Projects>().eq("is_del", "0"));
        model.addAttribute("projectsList", projectsList);
        return "/systemManage/userInfo";
    }

    @Override
    public AjaxResult getUserData(Map<String, Object> param) {
        int page = PublicConst.DEF_PAGE;
        int limit = PublicConst.DEF_LIMIT;
        QueryWrapper<Users> query = new QueryWrapper<Users>()
                .orderByAsc("username")
                .eq("is_del", "0");
        for (Object obj : param.keySet()) {
            String key = obj.toString();
            String keyValue = param.getOrDefault(key, "").toString();
            switch (key) {
                case "page":
                    page = Integer.parseInt(keyValue);
                    break;
                case "limit":
                    limit = Integer.parseInt(keyValue);
                    break;
                default:
                    query.eq(key, keyValue);
                    break;
            }
        }
        Page<Users> data = page(new Page<>(page, limit), query);
        return AjaxResult.success(data.getRecords(), data.getTotal());
    }

    @Override
    public String setUserInfo(Model model, String id) {
        Users user = getById(id);
        if (user == null) {
            user = new Users();
        }
        model.addAttribute("data", user);
        return "/systemManage/setUserInfo";
    }

    @Override
    public AjaxResult saveUserInfo(HttpSession session, Users data) {
        if (data == null) {
            return AjaxResult.error("没有可保存的用户数据");
        }
        LoginUserInfo loginUser = redis.get(session.getId());
        if (loginUser == null) {
            return AjaxResult.error("没有找到登录用户信息，请重新登录");
        }

        //RegInfo regInfo = registerInfoService.getRegInfo();

        if (data.getId() == null || data.getId().isEmpty()) {
            data.setId(snowFlakeID.getId());
            data.setCreator(loginUser.getUserName());
            data.setCreateTime(LocalDateTime.now());
            data.setEnabled("0");
            data.setCtime(String.valueOf(System.currentTimeMillis()));
            data.setIsSys("0");
            data.setFtpPwd("dag2025#");
        } else {
            data.setUpdater(loginUser.getUserName());
            data.setUpdateTime(LocalDateTime.now());
        }

        Users oldData = getById(data.getId());
        if (count(new QueryWrapper<Users>()
                .eq("username", data.getUsername())
                .eq("is_del", "0")
                .ne("id", data.getId())) > 0) {
            return AjaxResult.error("该用户账号已存在");
        }

        /*
        if (count(new QueryWrapper<Users>().ne("role_id", PublicConst.USER_ROLE_ID)) > regInfo.getUsers() + 4) {
            return CommonResult.failed("您的用户数量已超过授权数，不可保存");
        }
        */


        DataSourceTransactionManager tran = new DataSourceTransactionManager(jdbcTemplate.getDataSource());
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus update = tran.getTransaction(def);
        try {
            if (saveOrUpdate(data)) {
                //重启FTP
                if (oldData != null && oldData.getFtpPwd() != null && !oldData.getFtpPwd().equals(data.getFtpPwd())) {

                }

                ///    ftpServer.stop();
                //    ftpServer.start();
            }
            tran.commit(update);
            return AjaxResult.success("保存数据成功");
        } catch (Throwable e) {
            if (tran.isEnforceReadOnly()) {
                tran.rollback(update);
            }
            log.error(e.getMessage(), e);
            return AjaxResult.error("保存数据失败");
        }
    }

    @Override
    public AjaxResult delUserInfo(HttpSession session, String id) {
        LoginUserInfo loginUser = redis.get(session.getId());
        Users data = getById(id);
        if (data == null) {
            return AjaxResult.error("没有找到需要删除的用户信息");
        }
        if (data.getIsSys().equals("1")) {
            return AjaxResult.error("该用户为系统用户，不可删除");
        }
        if (data.getIsDel().equals("1")) {
            return AjaxResult.error("该用户已删除");
        }
        return update(new UpdateWrapper<Users>()
                .set("is_del", "1")
                .eq("id", id)) ? AjaxResult.success("删除登录用户成功") : AjaxResult.error("删除登录用户失败");
    }

    @Override
    public AjaxResult setUserEnabled(HttpSession session, Map<String, Object> params) {
        LoginUserInfo loginUser = redis.get(session.getId());
        if (loginUser == null) {
            return AjaxResult.error("没有找到登录用户信息，请重新登录");
        }
        String id = params.getOrDefault("id", "").toString();
        String action = params.getOrDefault("action", "0").toString();
        Users user = getById(id);
        if (user == null) {
            return AjaxResult.error("没有找到该用户信息");
        }
        if (user.getIsSys().equals("1")) {
            return AjaxResult.error("该用户为系统用户，不可设置本项");
        }
        DataSourceTransactionManager tran = new DataSourceTransactionManager(jdbcTemplate.getDataSource());
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus update = tran.getTransaction(def);
        //检测系统是否使用三员分管
        /*
        Security security = securityService.getOne(new QueryWrapper<Security>().last("limit 1"));
        if (security != null && "1".equals(security.getRbac())) {
            user.setIsExamine("1");
            examineLogService.NewExamineLog(loginUser, "用户(" + user.getUsername() + ")激活操作", "1", user.getId(), "secadmin");

        } else {
            user.setIsExamine("0");
        }
         */
        user.setEnabled(action);
        updateById(user);
        try {
            tran.commit(update);
            return AjaxResult.success("设置用户使用状态成功");
        } catch (Exception e) {
            tran.rollback(update);
            log.error(e.getMessage(), e);
            return AjaxResult.error("设置用户使用状态失败");
        }
    }

    @Override
    public AjaxResult resetPassword(HttpSession session, Map<String, Object> params) {
        LoginUserInfo loginUser = redis.get(session.getId());
        if (loginUser == null) {
            return AjaxResult.error("没有找到登录用户信息，请重新登录");
        }
        String id = params.getOrDefault("id", "").toString();
        Users user = getById(id);
        if (user == null) {
            return AjaxResult.error("没有找到该用户信息");
        }
        if (user.getIsSys().equals("1")) {
            return AjaxResult.error("该用户为系统用户，不可设置本项");
        }

        //检测系统是否使用三员分管
        DataSourceTransactionManager tran = new DataSourceTransactionManager(jdbcTemplate.getDataSource());
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus update = tran.getTransaction(def);
        //检测系统是否使用三员分管
        /*
        Security security = securityService.getOne(new QueryWrapper<Security>().last("limit 1"));
        if (security != null && "1".equals(security.getRbac())) {
            user.setIsExamine("1");
            examineLogService.NewExamineLog(loginUser, "用户(" + user.getUsername() + ")重置密码操作", "1", user.getId(), "secadmin");
        } else {
            user.setIsExamine("0");
        }

         */
        //生成密码
        String pwd = PublicUtil.getRandomString(8);
        String md5pwd = DigestUtils.md5DigestAsHex(pwd.getBytes(StandardCharsets.UTF_8));
        md5pwd = DigestUtils.md5DigestAsHex((Constants.PM_CHECK_CODE + md5pwd).getBytes())
                + user.getCtime();
        //加密
        String newPwd = BCrypt.encrypt(md5pwd);

        //设置暂停使用
        user.setPassword(newPwd);
        updateById(user);
        try {
            tran.commit(update);
            return AjaxResult.success("用户密码重置成功，请记住新密码：【" + pwd + "】");
        } catch (Throwable e) {
            tran.rollback(update);
            return AjaxResult.error("用户密码重置失败，错误信息：" + e.getMessage());
        }
    }

    @Override
    public AjaxResult setNewPassword(HttpSession session, String oldPwd, String newPwd) {
        LoginUserInfo loginUser = redis.get(session.getId());
        if (loginUser == null) {
            return AjaxResult.error("没有找到登录用户信息，请重新登录");
        }
        if (oldPwd.isEmpty() || newPwd.isEmpty()) {
            return AjaxResult.error("用户原密码、新密码不可为空");
        }

        Users userinfo = getOne(new QueryWrapper<Users>()
                .eq("username", loginUser.getUserCode())
                .eq("is_del", "0")
                .last("limit 1"));
        if (userinfo == null) {
            return AjaxResult.error("您的用户信息不存在");
        }

        String pwd = oldPwd + userinfo.getCtime();
        if (!BCrypt.matches(pwd, userinfo.getPassword())) {
            return AjaxResult.error("您的旧密码不正确，请重新输入");
        }


        //生成密码
        String md5pwd = newPwd + userinfo.getCtime();
        //加密
        newPwd = BCrypt.encrypt(md5pwd);
        //设置暂停使用
        userinfo.setPassword(newPwd);
        userinfo.setPwdModifyDate(System.currentTimeMillis() / 1000);
        return updateById(userinfo) ? AjaxResult.success("修改用户密码成功，请记住新密码") : AjaxResult.error("修改用户密码失败");
    }

    @Override
    public String setUserRole(Model model, String userId) {
        Users user = getOne(new QueryWrapper<Users>()
                .eq("id", userId)
                .eq("is_del", "0"));
        if (user == null) {
            model.addAttribute("message", "没有找到该用户的数据");
            return "/home/error";
        }
        if (user.getIsSys().equals("1")) {
            model.addAttribute("message", "该用户为系统账号，不可设置此功能");
            return "/home/error";
        }
        model.addAttribute("userId", userId);
        return "/systemManage/setUserRole";
    }

    @Override
    public AjaxResult getUserRole(HttpSession session, String userId) {
        List<Role> roles = roleService.list(new QueryWrapper<Role>()
                .eq("is_sys", "0")
                .eq("is_enable", "1"));
        List<UserRole> userRoles = userRoleService.list(new QueryWrapper<UserRole>().eq("user_id", userId));
        List<Transfer> transfers = new ArrayList<>();
        List<String> values = new ArrayList<>();
        for (Role role : roles) {
            Transfer transfer = new Transfer();
            transfer.setValue(role.getId());
            transfer.setTitle(role.getRemark());
            transfer.setChecked("");
            transfer.setDisabled("");
            transfers.add(transfer);
        }
        for (UserRole userRole : userRoles) {
            values.add(userRole.getRoleId());
        }
        List<Object> data = new ArrayList<>();
        data.add(transfers);
        data.add(values);
        return AjaxResult.success(data);
    }

    @Override
    public AjaxResult saveUserRole(HttpSession session, JSONObject params) {
        String userId = params.getOrDefault("userId", "").toString();
        String action = params.getOrDefault("action", "0").toString();
        JSONArray dataArray = params.getJSONArray("data");
        List<Transfer> transfers = JSON.parseArray(dataArray.toJSONString(), Transfer.class);

        Users user = getOne(new QueryWrapper<Users>()
                .eq("id", userId)
                .eq("is_del", "0"));
        if (user == null) {
            return AjaxResult.error("没有找到该用户的数据");
        }
        if (user.getIsSys().equals("1")) {
            return AjaxResult.error("该用户为系统账号，不可设置此功能");
        }

        DataSourceTransactionManager tran = new DataSourceTransactionManager(jdbcTemplate.getDataSource());
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus update = tran.getTransaction(def);

        switch (action) {
            case "0":
                for (Transfer transfer : transfers) {
                    if (userRoleService.count(new QueryWrapper<UserRole>()
                            .eq("user_id", userId)
                            .eq("role_id", transfer.getValue())) == 0) {
                        UserRole userRole = new UserRole();
                        userRole.setId(snowFlakeID.getId());
                        userRole.setUserId(userId);
                        userRole.setRoleId(transfer.getValue());
                        userRoleService.save(userRole);
                    }
                }
                break;
            case "1":
                for (Transfer transfer : transfers) {
                    userRoleService.remove(new QueryWrapper<UserRole>()
                            .eq("user_id", userId)
                            .eq("role_id", transfer.getValue()));
                }
                break;
        }
        try {
            tran.commit(update);
            return AjaxResult.success("设置用户角色成功");
        } catch (Throwable e) {
            tran.rollback(update);
            return AjaxResult.error("设置用户角色失败，错误信息：" + e.getMessage());
        }
    }

    @Override
    public AjaxResult RestUserLogin(HttpServletRequest request, LoginUser userInfo, HttpSession session) {
        if ("".equals(userInfo.getAccount()) ||
                "".equals(userInfo.getPassword())) {
            return AjaxResult.error("用户账号、密码不可为空");
        }

        //用户信息检测
        Users user = getOne(new QueryWrapper<Users>()
                .eq("username", userInfo.getAccount())
                .eq("is_del", "0")
                .last("limit 1"));
        if (user == null) {
            return AjaxResult.error("用户不存在或密码错误");
        }
        //检测密码
        String pwd = userInfo.getPassword() + user.getCtime();
        String hashPass = BCrypt.encrypt(pwd);
        if (!BCrypt.matches(pwd, user.getPassword())) {
            return AjaxResult.error("用户不存在或密码错误");
        }
        if (!"1".equals(user.getEnabled())) {
            return AjaxResult.error("您的帐号还没有激活，请与管理员联系");
        }

        //获取用户角色
        List<UserRole> userRoles = userRoleService.list(new QueryWrapper<UserRole>().eq("user_id", user.getId()));
        Collection<String> roles = new ArrayList<>();
        for (UserRole userRole : userRoles) {
            roles.add(userRole.getRoleId());
        }

        String spm = TCUtil.getOrderNo("SP.");
        LoginUserInfo loginUserInfo = new LoginUserInfo();
        loginUserInfo.setUserCode(user.getUsername());
        loginUserInfo.setUserId(user.getId());
        loginUserInfo.setUserName(user.getRealName());
        loginUserInfo.setIp(TCUtil.getClientIp(request));

        loginUserInfo.setRoles(roles);
        loginUserInfo.setSpm(spm);
        redis.set(session.getId(), loginUserInfo, 3600L);

        session.setAttribute("spm", spm);

        Map<String, String> map = new HashMap<>();
        map.put("username", user.getUsername());
        map.put("realName", user.getRealName());
        map.put("spm", spm);
        map.put("token", session.getId());
        map.put("url", "/");
        user.setLastLoginIp(TCUtil.getClientIp(request));
        user.setLastLoginTime(LocalDateTime.now());
        updateById(user);
        loginLogsService.loginLog(request, userInfo.getAccount(), "用户登录成功", "成功");
        return AjaxResult.success(map);
    }


    private boolean getUserLoginErr(String account) {
        Map<String, Integer> map = redis.get(account + LOGIN_COUNT);
        if (map == null) {
            return true;
        }
        int login_wait = map.get("login_wait");
        int login_num = map.get("login_num");
        if (login_wait > 0 && login_num >= 3) {
            int login_time = map.get("login_time");
            int now = (int) (System.currentTimeMillis() / 1000);
            return now - login_time >= login_wait * 60;
        }
        return true;
    }
}
